﻿using System;
using System.Collections;
using System.ComponentModel;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Windows.Forms;
using System.Xml.Serialization;
using CXMLStudioIDE.Common.Entities;
using CXMLStudioIDE.Common.EntityProperties;

namespace CXMLStudioIDE.WinFormsUI.CXMLStudioControls
{
    [Serializable]
    public class CXMLTreeNode : TreeNode, IDisposable
    {
        #region declarations 

        private readonly Component _Component = new Component();
        [NonSerialized] private ArrayList _BindingNameArray;
        [NonSerialized] private BusinessEntity _BusinessEntity;
        private bool _Disposed;
        [NonSerialized] private Type _EntityType;
        [NonSerialized] private DictionaryEntry _NodeEntry;
        [NonSerialized] private PropertySetCollection _PropertySetCollection;

        // Other managed resource this class uses.

        #endregion

        #region public properties
        /** IMPORTANT: Any property added here must be added in the Clone() method
         * override in this same class so the values are kept when cloning nodes **/
        [XmlIgnore]
        public Type EntityType
        {
            get { return _EntityType; }
            set { _EntityType = value; }
        }

        [XmlIgnore]
        public PropertySetCollection PropertySetCollection
        {
            get { return _PropertySetCollection; }
            set { _PropertySetCollection = value; }
        }

        public string PropertyType { get; set; }
        public string BindingName { get; set; }
        public string Lineage { get; set; }
        public int Top { get; set; }
        public int Left { get; set; }
        public int Bottom { get; set; }
        public int X { get; set; }
        public int Y { get; set; }

        [XmlIgnore]
        public BusinessEntity BusinessEntity
        {
            get { return _BusinessEntity; }
            set { _BusinessEntity = value; }
        }

        public bool IsAttribute { get; set; }
        public bool DisplayInForm { get; set; }
        public string GoldInstanceDescription { get; set; }

        #endregion

        #region Constructors

        public CXMLTreeNode()
        {
            DisplayInForm = true;
            PropertySetCollection = new PropertySetCollection();
        }

        public CXMLTreeNode(string text)
            : base(text)
        {
            DisplayInForm = true;
            PropertySetCollection = new PropertySetCollection();
        }

        public CXMLTreeNode(string text, CXMLTreeNode[] nodes)
            : base(text, nodes)
        {
            DisplayInForm = true;
            PropertySetCollection = new PropertySetCollection();
        }

        protected CXMLTreeNode(SerializationInfo info, StreamingContext context) :
            base(info, context)
        {
            DisplayInForm = true;
        }

        #endregion

        #region Methods

        //Gets the node lineage Parent_child_child... representing the EntityTypeVersionID's
        //At each level
        public string GetNodeLineage()
        {
            if (BusinessEntity != null)
            {
                _BindingNameArray = new ArrayList();
                Lineage = BusinessEntity.EntityTypeVersions.EntityTypeVersionsID.ToString();
                GetNodeLineage(this);
            }
            return Lineage;
        }

        private void SetBindingName()
        {
            if (_BindingNameArray.Count > 0)
            {
                for (int i = _BindingNameArray.Count - 1; i >= 0; i--)
                {
                    BindingName += _BindingNameArray[i];
                    BindingName += ".";
                }
                BindingName += Name;
            }
            else
            {
                BindingName += Name;
            }
            //Debug.WriteLine("BindingName:" + BindingName);
        }

        private void GetNodeLineage(CXMLTreeNode node)
        {
            if (node.Parent != null)
            {
                if (((CXMLTreeNode) node.Parent).DisplayInForm)
                {
                    _BindingNameArray.Add(node.Parent.Name);
                }
                var parent = node.Parent as CXMLTreeNode;
                if (parent.BusinessEntity != null)
                {
                    Lineage += "_" + ((CXMLTreeNode) node.Parent).BusinessEntity.EntityTypeVersions.EntityTypeVersionsID;
                }
                else
                {
                    //Debug.WriteLine(parent.Name + "|" + parent.FullPath );
                }
                GetNodeLineage(node.Parent as CXMLTreeNode);
            }
            else
            {
                string[] s = Lineage.Split('_');
                Lineage = "";
                string tmp = "";
                if (s.Length > 0)
                {
                    for (int i = 0; i < s.Length; i++)
                    {
                        if (i > 0 && i < s.Length && !tmp.Equals(s[(s.Length - 1) - i]))
                        {
                            Lineage += "_";
                        }
                        if (!tmp.Equals(s[(s.Length - 1) - i]))
                            Lineage += s[(s.Length - 1) - i];
                        tmp = s[(s.Length - 1) - i];
                    }
                }
                SetBindingName();
            }
        }

        //Returns the las EntityTypeVersionsID from a lineage. 
        public static int GetEntityTypeVersionsIdFromLineage(string lineage)
        {
            int i_EntityTypeVersionsID = -1;
            if (lineage.Contains("_"))
            {
                string[] etvids = lineage.Split('_');
                string sz_etvid = null;

                if (etvids.Length > 0)
                {
                    sz_etvid = etvids.GetValue(etvids.Length - 1).ToString();
                    i_EntityTypeVersionsID = Convert.ToInt32(sz_etvid);
                }
            }
            return i_EntityTypeVersionsID;
        }

        public override object Clone()
        {
            var CloneValue = (CXMLTreeNode) base.Clone();
            CloneValue.BindingName = BindingName;
            CloneValue._BusinessEntity = _BusinessEntity;
            CloneValue.DisplayInForm = DisplayInForm;
            CloneValue._EntityType = _EntityType;
            CloneValue.Bottom = Bottom;
            CloneValue.IsAttribute = IsAttribute;
            CloneValue.Left = Left;
            CloneValue.Lineage = Lineage;
            CloneValue.PropertyType = PropertyType;
            CloneValue.Top = Top;
            CloneValue.X = X;
            CloneValue.Y = Y;
            CloneValue.GoldInstanceDescription = GoldInstanceDescription;
            return CloneValue;
        }

        #endregion

        // Implement IDisposable.
        // Do not make this method virtual.
        // A derived class should not be able to override this method.

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
            // This object will be cleaned up by the Dispose method.
            // Therefore, you should call GC.SupressFinalize to
            // take this object off the finalization queue 
            // and prevent finalization code for this object
            // from executing a second time.
            GC.SuppressFinalize(this);
        }

        #endregion

        // Dispose(bool disposing) executes in two distinct scenarios.
        // If disposing equals true, the method has been called directly
        // or indirectly by a user's code. Managed and unmanaged resources
        // can be disposed.
        // If disposing equals false, the method has been called by the 
        // runtime from inside the finalizer and you should not reference 
        // other objects. Only unmanaged resources can be disposed.
        private void Dispose(bool disposing)
        {
            // Check to see if Dispose has already been called.
            if (!_Disposed)
            {
                // If disposing equals true, dispose all managed 
                // and unmanaged resources.
                if (disposing)
                {
                    // Dispose managed resources.
                    _Component.Dispose();
                }

                // Call the appropriate methods to clean up 
                // unmanaged resources here.
                // If disposing is false, 
                // only the following code is executed.
                //CloseHandle(handle);
                //handle = IntPtr.Zero;            
            }
            _Disposed = true;
        }

        // Use interop to call the method necessary  
        // to clean up the unmanaged resource.
        [DllImport("Kernel32")]
        private static extern Boolean CloseHandle(IntPtr handle);

        // Use C# destructor syntax for finalization code.
        // This destructor will run only if the Dispose method 
        // does not get called.
        // It gives your base class the opportunity to finalize.
        // Do not provide destructors in types derived from this class.
        ~CXMLTreeNode()
        {
            // Do not re-create Dispose clean-up code here.
            // Calling Dispose(false) is optimal in terms of
            // readability and maintainability.
            Dispose(false);
        }
    }
}